/*
 *  $Id: Game.cs 1246 2011-03-23 14:19:35Z thenn.erannor $
 *  This file is a part of Ragima CCG.
 *  (C) 2008 Ragima development group.
 *
 *  Licensed under the terms of the GNU GPL License version 2.
 *
 */

using System;
using System.Collections.Generic;

using AgateLib.DisplayLib;
using AgateLib.InputLib;
using AgateLib.Geometry;

namespace Ragima {
    public class Game: OldScene {
        private static Game _current;
        public static Game Current { get { return _current; } }

        public static void NewGame(GameMode mode, int seed) {
            _current = new Game(mode);
            Application.CurrentScene = _current;
            _current.SetRandom(seed);
            _current.StartGame();
        }

        private GameMode _mode;
        private Randomizer _random;
        private Player _player1, _player2;
        private int _meditation;
        private int _turn, _phase;
        private bool _side;
        private bool _fastPhase;
        private GamePhases _phases;
        private GameLog _log;
        private BattleNotice _notice;
        private TurnsAdvert _advert;
        private Dice _dice;

        private Region[] _stdRegions;
        //private TabCloser _tabCloser;
        private TargetType _target;
        private AbstractObject _prevSelection;
        private TabType _prevTab;

        private Game(GameMode mode) {
            _mode = mode;
            _random = Randomizer.Global;
            _player1 = new Player(this, _mode.Player1);
            _player2 = new Player(this, _mode.Player2);
            _player1.Enemy = _player2;
            _player2.Enemy = _player1;
            _meditation = Constants.MaxMeditation;
            _turn = 1;
            _phase = 0;
            _side = false;
            _fastPhase = false;
            _phases = new GamePhases();
            _log = new GameLog();
            _notice = new BattleNotice();
            _advert = new TurnsAdvert();
            _dice = new Dice();
            _stdRegions = new Region[] {
                new PlayerAvatarRegion(false),
                new PlayerAvatarRegion(true),
                new PlayerHandArrowRegion(Constants.ArrowLeft, false),
                new PlayerHandArrowRegion(Constants.ArrowRight, true),
                _phases
            };
            ////_tabCloser = new TabCloser();
        }

        public GameMode Mode { get { return _mode; } }

        public Randomizer Random { get { return _random; } }

        public Player MainPlayer { get { return _mode.GetMainPlayer(_side, _player1, _player2); } }
        public Player EnemyPlayer { get { return MainPlayer.Enemy; } }
        public Player MasterPlayer { get { return _mode.Master ? _player1 : _player2; } }
        public Player SlavePlayer { get { return _mode.Master ? _player2 : _player1; } }
        public Player CurrentPlayer { get { return _side ? _player2 : _player1; } }
        public Player WaitingPlayer { get { return _side ? _player1 : _player2; } }
        public bool IsMainCurrent { get { return MainPlayer == CurrentPlayer; } }

        public int Meditation { get { return _meditation; } set { _meditation = value; } }

        public int Turn { get { return _turn; } }

        public bool FastPhase { get { return _fastPhase; } }

        public GameLog Log { get { return _log; } }
        public IHelpProvider Help { get { return _log.Help; } set { _log.Help = value; } }
        public BattleNotice Notice { get { return _notice; } }
        public Dice Dice { get { return _dice; } }

        public override void InitializeScene() {
            base.InitializeScene();
            RegisterHotKey(KeyCode.F1, Application.NewGame);
            RegisterHotKey(KeyCode.F2, GameDeckChanger.ChangeElement1);
            RegisterHotKey(KeyCode.F3, GameDeckChanger.ChangeElement2);
            RegisterHotKey(KeyCode.F4, SaveLog);
        }
        private void SaveLog() {
            Log.Save("ragima.log", false);
        }

        protected override void ClickHandler(Region region, bool down) {
            if (region != null && down && HasTarget && _target.AcceptRegion(region))
                _target.ClickAcceptedRegion(region);
            else
                base.ClickHandler(region, down);
        }
        protected override void SecondaryClickHandler(Region region, bool down) {
            if (down) {
                if (HasTarget)
                    _target.Cancel();
                else
                    MainPlayer.ResetSelection();
            }
        }

        public IEnumerable<Player> Players {
            get {
                yield return _player1;
                yield return _player2;
            }
        }

        public bool GetPlayerId(Player player) {
            return player.IsMaster;
        }
        public Player GetPlayerById(bool id) {
            return id ? MasterPlayer : SlavePlayer;
        }

        public TargetType Target {
            get { return _target; }
        }

        public bool HasTarget {
            get { return _target != null; }
        }

        public void SetTarget(TargetRequest request) {
            _target = request.GetTarget();
            if (_target != null) {
                _target.Initialize(request);
                _prevSelection = request.Player.SelectedObject;
                _prevTab = request.Player.Tabs.Current.Content.Type;
                _target.AutoSelectTab();
            }
            UpdateTargetNotice();
        }

        public void ResetTarget() {
            _target = null;
            UpdateTargetNotice();
        }

        public void RestoreSelection(Player player) {
            ResetTarget();
            player.DefaultTab = _prevTab;
            player.SelectedObject = _prevSelection;
        }

        public void UpdateTargetNotice() {
            string text = HasTarget ? _target.NoticeText : string.Empty;
            if (!string.IsNullOrEmpty(text))
                _notice.Show(Resources.Game.TargetNotice.GetText(text));
            else if (_notice.Time == 0)
                _notice.Hide();
        }

        public void ErrorNotice(string notice) {
            Notice.Show(notice, 150);
        }

        public void SetRandom(int seed) {
            _random = new Randomizer(seed);
        }

        public void StartGame() {
            _side = (_random.Next(2) == 0) ^ _mode.Master;
            //_tabCloser.Show(!IsMainCurrent);
            StartGameForPlayer(MasterPlayer);
            StartGameForPlayer(SlavePlayer);
            UpdateRegions();
            Log.Add(Resources.Game.StartGame);
            _advert.SetAdvert(IsMainCurrent, true);
            _phases.Change(IsMainCurrent ? GamePhaseIcon.Swords : GamePhaseIcon.SwordsInSheath);
            Log.CurrentItem = _side ? AffectIcon.Player2OffencePhase : AffectIcon.Player1OffencePhase;
            CurrentPlayer.StartOffence(true);
        }

        private void StartGameForPlayer(Player player) {
            player.MainDeck.Shuffle(_random);
            player.StartCardsToHands();
            player.UpdateTabs();
        }

        public void EndPhase() {
            MainPlayer.ResetSelection();
            ResetTarget();
            PhaseIncrement();
            if (CurrentPlayer.State == PlayerState.Defence) {
                DisableControl();
                _phases.Change(MainPlayer.State == PlayerState.Offence ? GamePhaseIcon.Attacker : GamePhaseIcon.Defenders);
                Log.CurrentItem = AffectIcon.BattlePhase;
                DoBattle(WaitingPlayer);
                _advert.SetAdvert(IsMainCurrent, true);
                _phases.Change(IsMainCurrent ? GamePhaseIcon.Swords : GamePhaseIcon.SwordsInSheath);
                Log.CurrentItem = _side ? AffectIcon.Player2OffencePhase : AffectIcon.Player1OffencePhase;
                CurrentPlayer.StartOffence(false);
                EnableControl();
            } else {
                CurrentPlayer.EndOffence();
                bool hasNoAttackers = CurrentPlayer.GetAttackerCount() == 0;
                ChangeCurrentPlayer();
                _advert.SetAdvert(IsMainCurrent, false);
                if (hasNoAttackers)
                    _fastPhase = true;
                else
                    _phases.Change(IsMainCurrent ? GamePhaseIcon.SwordAndShield : GamePhaseIcon.SwordsInSheath);
                Log.CurrentItem = _side ? AffectIcon.Player2DefencePhase : AffectIcon.Player1DefencePhase;
                CurrentPlayer.StartDefence();
                if (hasNoAttackers) {
                    EndPhase();
                    _fastPhase = false;
                }
            }
        }

        public void EndTurnClick() {
            if (Application.Config.Interface.AutoMeditation) {
                Player player = CurrentPlayer;
                foreach (AbilityCard ability in player.GetAbilities()) {
                    IAutoCast cast = ability as IAutoCast;
                    if (cast != null && cast.IsCategory(AutoCastCategory.Meditation) && player.CanUseAbility(ability, false)) {
                        TargetArgs args = cast.GetAutoArgs(player, player);
                        if (ability.IsPossibleAction(args)) {
                            Mode.AddEvent(new Events.UseAbility(ability, args));
                            return;
                        }
                    }
                }
            }
            Mode.AddEvent(new Events.EndPhase());
        }

        public void PhaseIncrement() {
            _phase++;
            if (_phase == 4) {
                _turn++;
                _phase = 0;
            }
        }

        public void ChangeCurrentPlayer() {
            _side = !_side;
            //_tabCloser.Show(!IsMainCurrent);
        }

        public void AddEvent(Events.AbstractEvent ev) {
            _mode.AddEvent(ev);
        }

        /// <summary>
        /// Отрисовка игрового поля
        /// </summary>
        public override void Draw() {
            Data.Background.Draw();
            Dice.Draw();
            _phases.DrawSpecial();
            Data.DiceEye.Draw(Constants.DiceEyeX, Constants.DiceEyeY);

            Player main = MainPlayer, enemy = EnemyPlayer;

            if (main.Tabs != null) main.Tabs.Draw();
            Data.FrontPanel.Draw();

            main.DrawArrows(false);
            enemy.DrawArrows(true);

/*
            Surface phase = Data.PhaseWait;
            if (EnabledControl && IsMainCurrent) {
                phase = main.State == PlayerState.Defence ? Data.PhaseDefence : Data.PhaseOffence;
            }
            phase.Draw(Constants.PhaseX, Constants.PhaseY);
            Data.Numbers01.DrawCenter(Constants.TurnX, Constants.TurnY, _turn);
*/
            DrawMeditation();
            main.DrawMeditation(0);
            enemy.DrawMeditation(1);
            main.DrawHealth(false);
            enemy.DrawHealth(true);
            main.DrawInfo(Constants.Avatar1X);
            enemy.DrawInfo(Constants.Avatar2X);
            main.DrawManas(Constants.PlayerMana2Y);
            enemy.DrawManas(Constants.PlayerMana1Y);

            DrawRegions();

            Notice.Draw();
            _advert.Draw();
            DrawHint();
        }

        private void DrawMeditation() {
            int m = Meditation, h = 0;
            if (m > 0) {
                if (m <= 7)
                    h = 2*m;
                else if (m <= 14)
                    h = 14+(m-7);
                else
                    h = 21+2*(m-14);
                int y = Constants.MeditationPoolSize - h;
                Rectangle src = new Rectangle(Constants.MeditationPoolSize, y, Constants.MeditationPoolSize, h);
                Rectangle dest = new Rectangle(Constants.MeditationPoolX, Constants.MeditationPoolY+y, Constants.MeditationPoolSize, h);
                Data.MeditationBars.Draw(src, dest);
            }
            Data.MeditationBars.Draw(Constants.MeditationPoolOverlaySrc, Constants.MeditationPoolOverlayDest);
        }

        protected override bool IsTargetRegion(Region region) {
            return HasTarget && _target.AcceptRegion(region);
        }

        private bool _processEvents = false;

        /// <summary>
        /// Обработка игрового процесса
        /// </summary>
        public override void Process() {
            if (!_processEvents) {
                _processEvents = true;
                _mode.ProcessEvents(this);
                _processEvents = false;
            }
            Dice.Process();
            CurrentPlayer.Process();
            WaitingPlayer.Process();
            if (MainPlayer.Tabs != null) MainPlayer.Tabs.Process();
            ProcessRegions();
            Notice.Process();
            _advert.Process();
            ProcessHint();
        }

        public void UpdateAll() {
            MainPlayer.UpdateTabs();
            EnemyPlayer.UpdateTabs();
            MainPlayer.SelectedObject.UpdateTarget(MainPlayer);
            MainPlayer.UpdateRegions();
            UpdateRegions();
        }

        /// <summary>
        /// Обработка процесса битвы
        /// </summary>
        /// <param name="attacker">Нападающий игрок</param>
        public void DoBattle(Player attacker) {
            Player defender = attacker.Enemy;

            // вызов событий о начале битвы
            attacker.OnBattleReady();
            defender.OnBattleReady();
            HealthChange();
            DoDeath();

            // обработка атак
            foreach (Creature cr in attacker.GetAttackers())
                DoAttack(cr, defender);

            // конец битвы
            Dice.Reset();
            defender.EndDefence();
            attacker.ResetAttack();
            defender.ResetDefence();
        }

        /// <summary>
        /// Обработка атаки
        /// </summary>
        /// <param name="attacker">Атакующее существо</param>
        /// <param name="enemy">Вражеский игрок</param>
        private void DoAttack(Creature attacker, Player enemy) {
            // начальное значение урона
            int damage = attacker.Damage;
            // добавление бонуса критического удара
            damage += attacker.Summary.GetCriticalBonus();
            // цикл по защитникам
            List<Creature> defenders = new List<Creature>(attacker.Defenders);
            foreach (Creature defender in defenders) {
                if (defender != null && defender.State == CreatureState.Defence) {
                    // обработка удара
                    DoStrike(attacker, defender, ref damage);
                    if (attacker.IsDeath) break;
                }
            }
            // обработка удара по игроку
            if (damage > 0) DoStrike(attacker, enemy, damage);
        }

        /// <summary>
        /// Обработка удара по защитнику
        /// </summary>
        /// <param name="attacker">Атакующее существо</param>
        /// <param name="defender">Защищающее существо</param>
        /// <param name="damage">Текущий урон</param>
        private void DoStrike(Creature attacker, Creature defender, ref int damage) {
            if (attacker.IsDeath || defender.IsDeath) return;
            // подготовка списка двигаюшихся существ
            List<Creature> movedCreatures = new List<Creature>();
            movedCreatures.Add(attacker);
            movedCreatures.Add(defender);
            // движение существ вперед
            DoMoveCreatures(movedCreatures, true);
            // проверка быстроты
            bool strike = true;
            if (attacker.HasFlag<Affects.Fast>()) {
                int dice = GetDiceNextValue();
                bool success = dice >= attacker.Summary.Fast.Current.Value;
                Log.Add(Affects.Fast.Try, attacker, defender, dice, Resources.Game.Success(success));
                if (!success) {
                    if (damage > 0) damage--;
                    strike = false;
                }
            } else
                Log.Add(Resources.Game.AttackCreature, attacker, defender);
            // нанесение удара
            if (strike) {
                // если защитник имеет первый удар, а атакующий не имеет, то защитник бьет первым
                if (defender.HasFlag<Affects.FirstStrike>() && !attacker.HasFlag<Affects.FirstStrike>()) {
                    Creature tmp = attacker;
                    attacker = defender;
                    defender = tmp;
                }
                if (!TestPreStrike(attacker, defender)) {
                    DoStrikeRedirect(attacker, defender, ref damage);
                    DoSingleStrike(attacker, defender, ref damage);
                    if (!defender.IsDeath) {
                        DoStrikeRedirect(defender, attacker, ref damage);
                        DoSingleStrike(defender, attacker, ref damage);
                    }
                }
            }
            // движение существ назад
            DoMoveCreatures(movedCreatures, false);
            // обработка смерти
            DoDeath();
        }

        /// <summary>
        /// Обработка удара по игроку
        /// </summary>
        /// <param name="attacker">Атакующее существо</param>
        /// <param name="enemy">Вражеский игрок</param>
        /// <param name="damage">Текущий урон</param>
        private void DoStrike(Creature attacker, Player enemy, int damage) {
            if (attacker.IsDeath) return;
            // подготовка списка двигаюшихся существ
            List<Creature> movedCreatures = new List<Creature>();
            movedCreatures.Add(attacker);
            // движение существ вперед
            Log.Add(Resources.Game.AttackPlayer, attacker);
            DoMoveCreatures(movedCreatures, true);
            // перехват ударов
            DoStrikeRedirect(attacker, enemy, ref damage);
            // нанесение удара
            if (damage > 0) {
                if (!TestFear(attacker, enemy)) {
                    int dmg = enemy.DamageBy(attacker.NewDamage(damage));
                    if (dmg > 0) attacker.OnHit(enemy, dmg);
                }
            }
            // обработка изменения здоровья
            HealthChange();
            // движение существ назад
            DoMoveCreatures(movedCreatures, false);
            // обработка смерти
            DoDeath();
        }

        /// <summary>
        /// Обработка перенаправления ударов
        /// </summary>
        /// <param name="hitter">Кто наносит удар</param>
        /// <param name="victim">По кому наносится удар</param>
        /// <param name="damage">Текущий урон</param>
        private void DoStrikeRedirect(Creature hitter, AbstractObject victim, ref int damage) {
            foreach (Affects.StrikeRedirect redirect in victim.Summary.StrikeRedirects) {
                Log.Add(Affects.StrikeRedirect.Action, redirect.To);
                DoSingleStrike(hitter, redirect.To, ref damage);
                if (damage == 0) break;
            }
        }

        /// <summary>
        /// Обработка одиночного удара
        /// </summary>
        /// <param name="hitter">Кто наносит удар</param>
        /// <param name="victim">По кому наносится удар</param>
        /// <param name="damage">Текущий урон</param>
        private void DoSingleStrike(Creature hitter, Creature victim, ref int damage) {
            // проверка страха
            if (TestFear(hitter, victim)) {
                if (hitter.State == CreatureState.Offence) damage = 0;
                return;
            }
            // нанесение урона
            int dmg = 0;
            if (!hitter.HasFlag<Affects.SlowAfterAttack>())
                dmg = hitter.State == CreatureState.Offence ? damage : hitter.Damage;
            dmg = victim.DamageBy(hitter.NewDamage(dmg));
            if (dmg > 0) hitter.OnHit(victim, dmg);
            // проверка добивания
            int fKill = hitter.Summary.GetFinishKill();
            if (fKill > 0 && victim.HealthToMin <= fKill)
                victim.DamageBy(hitter.NewDamage(fKill));
            // уменьшение текущего урона
            if (hitter.State == CreatureState.Offence) {
                if (!hitter.IsDeath && hitter.HasFlag<Affects.Brutal>())
                    damage = Math.Max(damage - dmg, 0);
                else
                    damage = 0;
            }
            // обработка изменения здоровья
            HealthChange();
            // проверка факта убийства и святости
            if (victim.IsDeath && hitter.HasFlag<Affects.Holy>())
                Affects.Holy.OnKill(hitter, victim);
        }

        /// <summary>
        /// Проверка перед ударом
        /// </summary>
        /// <param name="hitter">Кто наносит удар</param>
        /// <param name="victim">По кому наносится удар</param>
        /// <returns>true, если удар не нужен</returns>
        private bool TestPreStrike(Creature hitter, Creature victim) {
            if (TestSinglePreStrike(hitter, victim)) return true;
            if (TestSinglePreStrike(victim, hitter)) return true;
            return false;
        }

        /// <summary>
        /// Одиночная проверка перед ударом
        /// </summary>
        /// <param name="hitter">Кто наносит удар</param>
        /// <param name="victim">По кому наносится удар</param>
        /// <returns>true, если удар не нужен</returns>
        private bool TestSinglePreStrike(Creature hitter, Creature victim) {
            if (TestDeathStare(hitter, victim)) return true;
            if (TestEating(hitter, victim)) return true;
            return false;
        }

        /// <summary>
        /// Проверка взгляда смерти
        /// </summary>
        /// <param name="starer">Кто смотрит</param>
        /// <param name="victim">На кого смотрят</param>
        /// <returns>true, если взгляд смерти сработал</returns>
        private bool TestDeathStare(Creature starer, Creature victim) {
            if (starer.Summary.DeathStare.Empty) return false;
            bool success = starer.Summary.DeathStare.Current.DoTry(victim);
            if (success) victim.Death();
            return success;
        }

        /// <summary>
        /// Проверка поедания
        /// </summary>
        /// <param name="eater">Кто поедает</param>
        /// <param name="victim">Кого поедают</param>
        /// <returns>true, если поедание сработало</returns>
        private bool TestEating(Creature eater, Creature victim) {
            if (eater.Eating) return false;
            foreach (Affects.Eating eating in eater.Summary.Eating) {
                if (eating.CanEat(victim)) {
                    Log.Add(Affects.Eating.Action, eater, victim);
                    victim.Death();
                    eater.Eating = true;
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Проверка страха
        /// </summary>
        /// <param name="hitter">Кто наносит удар</param>
        /// <param name="victim">По кому наносится удар</param>
        /// <returns>true, если страх действителен</returns>
        private bool TestFear(AbstractObject hitter, AbstractObject victim) {
            if (hitter.HasFlag<Affects.Fearless>()) return false;
            if (!victim.Summary.FearAura.Empty && victim.Summary.FearAura.Current.Value >= hitter.MaxHealth) return true;
            if (!hitter.Summary.Fear.Empty && hitter.Summary.Fear.Current.Value >= hitter.Health) return true;
            return false;
        }

        /// <summary>
        /// Обработка движения существ для удара
        /// </summary>
        /// <param name="creatures">Список существ</param>
        /// <param name="forward">Направление движения</param>
        private void DoMoveCreatures(List<Creature> creatures, bool forward) {
            int maxCount = 0, step = 1;
            while (maxCount != creatures.Count) {
                maxCount = 0;
                foreach (Creature cr in creatures) {
                    if (forward) {
                        if (cr.ForwardStep == Constants.MaxAttackForwardStep)
                            maxCount++;
                        else {
                            cr.ForwardStep = Math.Min(cr.ForwardStep + step, Constants.MaxAttackForwardStep);
                            step++;
                        }
                    } else {
                        if (cr.ForwardStep == 0)
                            maxCount++;
                        else
                            cr.ForwardStep = Math.Max(cr.ForwardStep - step, 0);
                    }
                }
                NextFrame();
            }
        }

        /// <summary>
        /// Обработка изменений здоровья
        /// </summary>
        public void HealthChange() {
            DisableControl();
            while (HasHealthChange(CurrentPlayer) || HasHealthChange(WaitingPlayer))
                NextFrame();
            EnableControl();
        }
        private bool HasHealthChange(Player player) {
            if (player.IsHealthChanged) return true;
            foreach (Creature cr in player.GetCreatures()) if (cr.IsHealthChanged) return true;
            return false;
        }

        /// <summary>
        /// Обработка смерти
        /// </summary>
        public void DoDeath() {
            DisableControl();
            DoDeath(CurrentPlayer);
            DoDeath(WaitingPlayer);
            // обновление всего
            UpdateAll();
            EnableControl();
        }
        private void DoDeath(Player player) {
            foreach (Creature cr in player.GetCreatures()) if (cr.IsDeath) DoDeath(cr);
            List<Land> deathLands = player.Lands.FindAll(delegate(Land land) { return land.IsDeath; });
            foreach (Land land in deathLands) DoDeath(land);
        }
        private const byte FadingAlpha = 5;
        private void DoDeath(Creature cr) {
            Log.Add(Resources.Game.DeathCreature, cr);
            // затухание изображения существа
            DoDeathFading(cr, true);
            // проверка реинкарнации
            if (DoReincarnation(cr)) return;
            // удаление существа
            cr.Remove();
        }
        private bool DoReincarnation(Creature cr) {
            if (cr.IsInstantDeath || cr.MaxHealth <= 0 || !cr.HasFlag<Affects.Reincarnation>()) return false;
            int dice = GetDiceNextValue();
            bool success = dice >= cr.Summary.Reincarnation.Current.Value;
            Log.Add(Affects.Reincarnation.Try, cr, dice, Resources.Game.Success(success));
            if (!success) return false;
            cr.ResetDeath();
            cr.Health = cr.MaxHealth;
            // обратное появление изображения существа
            DoDeathFading(cr, false);
            return true;
        }
        private void DoDeathFading(Creature cr, bool fade) {
            cr.Alpha = fade ? Constants.MaxAlpha : (byte) 0;
            byte endAlpha = fade ? (byte) 0 : Constants.MaxAlpha;
            while (cr.Alpha != endAlpha) {
                if (fade) {
                    if (cr.Alpha < FadingAlpha) cr.Alpha = 0; else cr.Alpha -= FadingAlpha;
                } else {
                    if (cr.Alpha > (Constants.MaxAlpha - FadingAlpha)) cr.Alpha = Constants.MaxAlpha; else cr.Alpha += FadingAlpha;
                }
                NextFrame();
            }
        }
        private void DoDeath(Land land) {
            Log.Add(Resources.Game.DeathLand, land);
            land.Remove();
        }

        private const int PauseAfterDice = 50;
        /// <summary>
        /// Запрос следующего числа с дайса
        /// </summary>
        /// <returns>Выпавшее число</returns>
        public int GetDiceNextValue() {
            DisableControl();
            // запуск вращения дайса
            Dice.Next(_random);
            // ожидание остановки дайса
            while (!Dice.IsAnchored) NextFrame();
            // пауза после остановки дайса
            for (int i = 0; i < PauseAfterDice; i++) NextFrame();
            // возврат числа и сброс дайса
            int value = Dice.Value;
            Dice.Reset();
            EnableControl();
            return value;
        }

        /// <summary>
        /// Обновление списка регионов
        /// </summary>
        protected override void UpdateRegions(List<Region> regions) {
            regions.Add(_log);
            regions.AddRange(_stdRegions);
            if (MainPlayer.Tabs != null) {
                regions.AddRange(MainPlayer.Tabs.GetRegions());
                regions.AddRange(MainPlayer.Tabs.Current.Content.GetRegions());
            }
            regions.AddRange(MainPlayer.GetRegions());
            if (HasTarget && _target != null)
                regions.AddRange(_target.GetRegions());
            //regions.AddRange(_tabCloser.GetRegions());
        }

        /// <summary>
        /// Поиск региона по существу
        /// </summary>
        public Region FindRegionBy(Creature creature) {
            return FindRegion(delegate(Region region) { return region is PlayerCreatureRegion && ((PlayerCreatureRegion) region).Creature == creature; });
        }

        /// <summary>
        /// Поиск региона по местности
        /// </summary>
        public Region FindRegionBy(Land land) {
            return FindRegion(delegate(Region region) { return region is PlayerLandRegion && ((PlayerLandRegion) region).Land == land; });
        }

        /// <summary>
        /// Поиск региона по игроку
        /// </summary>
        public Region FindRegionBy(Player player) {
            return FindRegion(delegate(Region region) { return region is PlayerAvatarRegion && ((PlayerAvatarRegion) region).Player == player; });
        }
    }

    public static class GameDeckChanger {
        public static ElementType Element1 = ElementType.Neutral;
        public static ElementType Element2 = ElementType.Neutral;

        public static void ChangeElement1() {
            ChangeElement(Application.Config.Player1, ref Element1);
        }
        public static void ChangeElement2() {
            ChangeElement(Application.Config.Player2, ref Element2);
        }

        static void ChangeElement(PlayerInfo info, ref ElementType element) {
            int i = element.Number + 1;
            if (i == ElementType.Count) i = 0;
            element = ElementType.AllElements[i];
            info.Deck = CardDeck.CreateElementDeck(element);
            Game.Current.Log.Add(Resources.Deck.Select, info, info.Deck.Name);
        }
    }

    /// <summary>
    /// Регион, определяющий аватар игрока
    /// </summary>
    public class PlayerAvatarRegion: Region {
        private readonly static Color TargetColor = Color.FromArgb(48, 0, 255, 0);

        private bool _side;

        public PlayerAvatarRegion(bool side):
            base(new Rectangle(side ? Constants.Avatar2X : Constants.Avatar1X, Constants.AvatarY, Constants.AvatarSize, Constants.AvatarSize)) {
            _side = side;
        }

        public Player Player { get { return _side ? Game.Current.EnemyPlayer : Game.Current.MainPlayer; } }

        public override void Draw() {
            Player.DrawAvatar(Rect);
            DrawBorder();
        }

        public override void DrawTarget() {
            Player.DrawAvatar(Rect);
            Display.FillRect(Rect, TargetColor);
            DrawBorder();
        }

        private void DrawBorder() {
            Surface hero = _side ? Data.HeroRight : Data.HeroLeft;
            hero.Draw(Rect.Location);
        }

        public override void OnClick() {
            Game.Current.MainPlayer.SelectPlayer(Player);
        }
    }

    /// <summary>
    /// Регион, определяющий стрелку для листания карт в руке игрока
    /// </summary>
    public class PlayerHandArrowRegion: Region {
        private bool _dir;

        public PlayerHandArrowRegion(Rectangle rect, bool dir): base(rect) {
            _dir = dir;
            HotKey = _dir ? KeyCode.Right : KeyCode.Left;
        }

        public override void Draw() {
            TabContent content = Game.Current.MainPlayer.Tabs.Current.Content;
            bool need = _dir ? content.NeedScrollRight : content.NeedScrollLeft;
            Surface a = need ? Data.GetArrow(_dir).GetSurface(Focus) : Data.GetArrowDisabled(_dir);
            a.Draw(Rect.Location);
        }

        public override void OnClick() {
            TabContent content = Game.Current.MainPlayer.Tabs.Current.Content;
            bool need = _dir ? content.NeedScrollRight : content.NeedScrollLeft;
            if (!need) return;
            Data.Sounds.Events.Play("click");
            if (_dir)
                content.ScrollRight();
            else
                content.ScrollLeft();
        }
    }

    /// <summary>
    /// Регион, определяющий кнопку "Конец хода"
    /// </summary>
    public class EndTurnRegion: Region {
        public EndTurnRegion(): this(Constants.ButtonEndTurn) {}
        public EndTurnRegion(Rectangle rect): base(rect) {
            HotKey = KeyCode.Space;
        }

        public override void Draw() {
            Data.ButtonEndTurn.GetSurface(Focus).Draw(Rect.Location);
        }
        public override void OnClick() {
            Data.Sounds.Events.Play("click");
            if (!Game.Current.IsMainCurrent) return;
            Game.Current.EndTurnClick();
        }
    }
}
